﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Configuration;
using System.Net;
using System.Windows.Forms;
using System.Text;
using Newtonsoft.Json;

namespace PoolSide
{
    public class ExchangeInfo : IMarket
    {
        #region Delegates and Events
        public delegate void EventChanged(object sender, ExchangeInfoEventArgs e);
        public event EventChanged OnEventChanged;
        #endregion

        #region Private Member Variables
        private DateTime _LatestTrade;
        private decimal? _Ask;
        private decimal? _Close;
        private decimal? _Average;
        private decimal? _Low;
        private decimal? _Volume;
        private decimal? _High;
        private decimal? _Bid;
        private decimal? _BtcBalance;
        private decimal? _CurrencyBalance;
        private bool _IsApi;
        private string _Currency;
        private string _Symbol;
        private string _SiteUrl;
        private string _MarketName;
        private string _Username;
        private string _Password;
        private ApiInfo _Api;
        private string url;
        private static bool shade = false;
        #endregion

        #region Properties
        public bool IsApi
        {
            get { return _IsApi; }
            set { _IsApi = value; }
        }
        public DateTime LatestTrade
        {
            get { return _LatestTrade; }
            set { _LatestTrade = value; }
        }
        public decimal? Ask
        {
            get { return _Ask; }
            set { _Ask = value; }
        }
        public decimal? Close
        {
            get { return _Close; }
            set { _Close = value; }
        }
        public decimal? Avg
        {
            get { return _Average; }
            set { _Average = value; }
        }
        public decimal? Low
        {
            get { return _Low; }
            set { _Low = value; }
        }
        public decimal? Volume
        {
            get { return _Volume; }
            set { _Volume = value; }
        }
        public decimal? High
        {
            get { return _High; }
            set { _High = value; }
        }
        public decimal? Bid
        {
            get { return _Bid; }
            set { _Bid = value; }
        }
        public decimal? BtcBalance
        {
            get { return _BtcBalance; }
            set { _BtcBalance = value; }
        }
        public decimal? CurrencyBalance
        {
            get { return _CurrencyBalance; }
            set { _CurrencyBalance = value; }
        }
        public string Currency
        {
            get { return _Currency; }
            set { _Currency = value; }
        }
        public string Symbol
        {
            get { return _Symbol; }
            set 
            {
                // at this point, _Symbol will have the 3-char currency code at the end, so we 
                // need to strip that off:
                _Symbol = value.Substring(0, value.Length - 3);
                
                bool hasSetting = false;
                foreach (SettingsProperty sp in Properties.Market.Default.Properties)
                {
                    if (sp.Name == _Symbol)
                    {
                        hasSetting = true;
                        break;
                    }
                }

                if (!hasSetting)
                {
                    _Symbol = value; // Symbol isn't stored locally
                    _IsApi = false;
                }
                else
                {
                    string json = Properties.Market.Default[_Symbol].ToString().Replace("\r\n", "");

                    using (JsonTextReader jtr = new JsonTextReader(new StringReader(json)))
                    {
                        while (jtr.Read())
                        {
                            if (jtr.TokenType == JsonToken.PropertyName)
                            {
                                switch (jtr.Value.ToString().ToUpper().Trim())
                                {
                                    case "MARKETNAME": _MarketName = jtr.ReadAsString(); break;
                                    case "SITEURL": _SiteUrl = jtr.ReadAsString(); break;
                                    case "ISAPI":
                                        string input = jtr.ReadAsString();
                                        if (input == "1" || input.ToUpper() == "TRUE")
                                            _IsApi = true;
                                        else
                                            _IsApi = false;
                                        break;
                                    case "PASSWORD": _Password = jtr.ReadAsString(); break;
                                    case "USERNAME": _Username = jtr.ReadAsString(); break;
                                }
                            }
                        }
                    }
                }
            }
        }
        public bool IsUnavailable
        {
            get
            {
                return (_Low.HasValue && _High.HasValue);
            }
        }
        public string SiteUrl
        {
            get { return _SiteUrl; }
            set { _SiteUrl = value; }
        }
        public string MarketName
        {
            get { return _MarketName; }
            set { _MarketName = value; }
        }
        public string Username
        {
            get { return _Username; }
            set { _Username = value; }
        }
        public string Password
        {
            get { return _Password; }
            set { _Password = value; }
        }
        public ApiInfo Api
        {
            get 
            {
                if (_Api == null)
                {
                    string json = Properties.Api.Default[_Symbol].ToString();
                    _Api = new ApiInfo(json);
                }
                return _Api;
            }
            
        }
        #endregion

        #region Public Methods
        public ListViewItem GetListViewItem(string model)
        {
            ListViewItem item = new ListViewItem();
            item.Text = _Symbol;
            if (_Ask.HasValue) item.SubItems.Add(string.Format("{0:######0.00}", _Ask.Value));
            if (_Bid.HasValue) item.SubItems.Add(string.Format("{0:######0.00}", _Bid.Value));
            
            if (!_Low.HasValue || !_High.HasValue)
            {
                item.SubItems.Add("( unavailable )");
                item.SubItems.Add("( unavailable )");
            }
            else
            {
                item.SubItems.Add(string.Concat(
                    string.Format("{0:######0.00}",_Low), 
                    " - ", 
                    string.Format("{0:######0.00}", _High)));

                string displayAmount = "";
                decimal loAmt, hiAmt;
                if (model == "confirmed")
                {
                    loAmt = _Low.Value * Program.ConfirmedReward;
                    hiAmt = _High.Value * Program.ConfirmedReward;
                    
                    displayAmount = string.Concat(
                        (string.Format("{0:######0.00}", loAmt)),
                        " - ",
                        (string.Format("{0:######0.00}", hiAmt)));
                }
                else
                {
                    loAmt = _Low.Value * (Program.ConfirmedReward + Program.UnconfirmedReward);
                    hiAmt = _High.Value * (Program.ConfirmedReward + Program.UnconfirmedReward);

                    displayAmount = string.Concat(
                        (string.Format("{0:######0.00}", loAmt)),
                        " - ",
                        (string.Format("{0:######0.00}", hiAmt)));
                }

                item.SubItems.Add(displayAmount);
            }

            if (shade)
                item.BackColor = System.Drawing.Color.LightGray;
            shade = !shade;
            item.Tag = this;

            return item;
        }
        public void ExecuteBuy(decimal btcQuantity, decimal price)
        {
            SendEvent(string.Concat("Contacting ", _MarketName, " ..."));
        }
        public void ExecuteSell(decimal btcQuantity, decimal price)
        {

        }
        public bool LoadAccountInfo()
        {
            if (this.Api == null)
            {
                SendEvent("ERR: Cannot load API"); // temp stub for testing
                return false;
            }
            else
            {
                url = _Api.UrlAccount;
                url = FixApi(url);
                string val = Program.GetResponseText(url);
            }

            return true;
        }
        public bool LoadAccountInfo(string password)
        {
            if (this.Api == null)
            {
                SendEvent("ERR: Cannot load API"); // temp stub for testing
                return false;
            }
            else
            {
                url = _Api.UrlAccount;
                url = FixApi(url, password);
                string val = Program.GetResponseText(url).Replace("\"", "'");
            }

            return true;
        }
        public List<OrderbookEntry> GetOrderbookEntries()
        {
            string json = GetJson(_Api.UrlOrderbook);
            List<OrderbookEntry> entries = JsonConvert.DeserializeObject<List<OrderbookEntry>>(json);
            return entries;
        }
        public void SaveInfo()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("{");
            sb.AppendFormat("'MarketName':'{0}', ", _MarketName);
            sb.AppendFormat("'SiteUrl':'{0}', ", _SiteUrl);
            sb.AppendFormat("'IsApi':'{0}', ", _IsApi ? "1" : "0");
            sb.Append("'ApiUrl':'null', ");
            sb.AppendFormat("'ApiKey':'{0}', ", _Api.ApiKey);
            sb.Append("'QSFormat':'null', ");
            sb.AppendFormat("'Username':'{0}', ", _Username);
            if (_Password == null || _Password == "null")
                sb.Append("'Password':''}");
            else
                sb.AppendFormat("'Password':'{0}'}", _Password);

            Properties.Market.Default[_Symbol] = sb.ToString();
            Properties.Market.Default.Save();
        }
        #endregion

        #region Private Methods
        private void SendEvent(string message)
        {
            if (OnEventChanged != null)
            {
                OnEventChanged(this, new ExchangeInfoEventArgs(message));
            }
        }
        private string FixApi(string inputUrl)
        {
            /* Purpose of this method is to take an input URL stored in the .settings file and replace the 
             * '@nnn@' token placeholders with the actual values used for this market. Note that the token
             * placeholders are in all UPPERCASE in the .settings file! */

            string newUrl = inputUrl
                .Replace("@KEY@", _Api.ApiKey)
                .Replace("@TOKEN@", _Api.Token)
                .Replace("@USERNAME@", _Username)
                .Replace("@PASSWORD@", _Password);

            return newUrl;
        }
        private string FixApi(string inputUrl, string password)
        {
            /* Purpose of this method is to take an input URL stored in the .settings file and replace the 
             * '@nnn@' token placeholders with the actual values used for this market. Note that the token
             * placeholders are in all UPPERCASE in the .settings file! */

            string newUrl = inputUrl
                .Replace("@KEY@", _Api.ApiKey)
                .Replace("@TOKEN@", _Api.Token)
                .Replace("@USERNAME@", _Username)
                .Replace("@PASSWORD@", password);

            return newUrl;
        }
        private string FixApi(string inputUrl, decimal quantity, decimal price)
        {
            string newUrl = inputUrl
                .Replace("@KEY@", _Api.ApiKey)
                .Replace("@TOKEN@", _Api.Token)
                .Replace("@USERNAME@", _Username)
                .Replace("@PASSWORD@", _Password)
                .Replace("@QUANTITY@", quantity.ToString())
                .Replace("@PRICE@", price.ToString());

            return newUrl;
        }
        private string GetJson(string url)
        {
            var request = WebRequest.Create(url);
            string jsonResponse = "";
            using (var resp = (HttpWebResponse)request.GetResponse())
            {
                using (var reader = new StreamReader(resp.GetResponseStream()))
                {
                    jsonResponse = reader.ReadToEnd().Replace("\"", "'");
                }
            }
            return jsonResponse;
        }
        private void LoadAccountDataFromJson(string json)
        {
            /* Loads account information from json retrieved from various markets */

        }
        #endregion

        #region Constructors

        #endregion
    }

    public class ExchangeInfoEventArgs : EventArgs
    {
        private DateTime _Occurred;
        public string Message { get; set; }
        public DateTime Occurred
        {
            get { return _Occurred; }
        }
        public ExchangeInfoEventArgs() 
        {
            _Occurred = DateTime.Now;
        }
        public ExchangeInfoEventArgs(string message)
        {
            _Occurred = DateTime.Now;
            this.Message = message;
        }
    }
}
